home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 23
/
AACD 23.iso
/
AACD
/
Online
/
opennap
/
class.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-06-08
|
13KB
|
533 lines
/* Copyright (C) 2000-1 drscholl@users.sourceforge.net
* This is free software distributed under the terms of the
* GNU Public License. See the file COPYING for details.
*
* $Id: class.c,v 1.18 2001/02/15 08:39:45 drscholl Exp $
*
* Based on bans.c in part.
* oracle ip database idea taken from ircd
* is_address() taken from hybrid ircd and modified to return a complete
* ip.
* All this mess put together by Colten Edwards (q)
*/
/* Modified 30/05/01 : Added include "extrasocket.h" with inlines for a few
for Amiga port : bsdsocket.library functions not emulated by ixemul
Added define for htonl(), which is not defined in the
Amiga GCC 2.7.0 includes
*/
#define htonl(x) (x)
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "opennap.h"
#include "hashlist.h"
#include "debug.h"
#include "extrasocket.h"
#define F_SYNC (1<<0)
typedef struct
{
char *name;
LIST *list;
unsigned int flags;
unsigned int oracle[256];
}
acl_t;
static acl_t ilines;
static acl_t dlines;
static acl_t elines;
static acl_t limits;
#define check_oracle(acl,ip) (((acl)->oracle[(ip) & 0xff] & (ip)) == (ip))
int
check_class (CONNECTION * con, ip_info_t * info)
{
LIST *list;
CLASS *class;
int count, boot_em = 0;
count = info->users;
if (!count)
return count;
if (Max_Clones > 0 && count >= Max_Clones)
boot_em = count;
if (check_oracle (&limits, con->ip))
{
for (list = limits.list; list; list = list->next)
{
class = list->data;
if ((con->ip & class->mask) == (class->target & class->mask))
{
if (count >= class->limit)
return count;
else
return 0;
}
}
}
return boot_em;
}
/* determine whether a connection from this ip address is allowed by the acls
* defined.
*/
int
acl_connection_allowed (unsigned int ip)
{
if (ilines.list)
{
/* i:lines exist, don't allow any connections unless the ip
* matches
*/
if (!check_oracle (&ilines, ip))
return 0;
}
/* check for a d:line */
if (check_oracle (&dlines, ip))
{
/* check for an e:line */
if (check_oracle (&elines, ip))
return 1;
return 0;
}
return 1;
}
static int
generic_acl_save (acl_t * acl)
{
FILE *fp;
LIST *list;
access_t *b;
char path[_POSIX_PATH_MAX];
char maskstr[sizeof ("/xxx.xxx.xxx.xxx")];
snprintf (path, sizeof (path), "%s/%s", Config_Dir, acl->name);
if (acl->list == 0)
{
unlink (path);
return 0;
}
#ifdef WIN32
#define LE "\r\n"
#else
#define LE "\n"
#endif
if ((fp = fopen (path, "w")) == 0)
return -1;
fprintf (fp,
"# DO NOT EDIT THIS FILE! - automatically generated by opennap%s",
LE);
fprintf (fp, "# Hi Mom, We got %s!%s", acl->name, LE);
for (list = acl->list; list; list = list->next)
{
struct in_addr in;
b = list->data;
maskstr[0] = 0;
if (b->mask != 0xffffffff)
{
in.s_addr = BSWAP32 (b->mask);
snprintf (maskstr, sizeof (maskstr), "/%s", inet_ntoa (in));
}
in.s_addr = BSWAP32 (b->ip);
fprintf (fp, "%s%s %3d%s", inet_ntoa (in), maskstr, b->count, LE);
}
if (fclose (fp))
{
logerr ("generic_acl_save", "fclose");
return -1;
}
return 0;
}
void
acl_save (void)
{
generic_acl_save (&limits);
generic_acl_save (&ilines);
generic_acl_save (&dlines);
generic_acl_save (&elines);
}
static int
compare_ipmask (unsigned int a, unsigned int amask, unsigned int b,
unsigned int bmask)
{
int r = -2;
/* always use the smallest mask to compare */
unsigned int usemask = (amask > bmask) ? bmask : amask;
/*
struct in_addr in;
in.s_addr = ntohl(a);
printf("comparing %s(%u) and", inet_ntoa(in), a);
in.s_addr = ntohl(b);
printf(" %s(%u) = ",inet_ntoa(in), b);
in.s_addr = ntohl(usemask);
printf(" using mask=%s ", inet_ntoa(in));
*/
if ((a & usemask) == (b & usemask))
{
/*printf("masks are equal "); */
/* the larger mask (more specific) should sort first */
if (bmask != amask)
r = (amask > bmask) ? 1 : -1;
}
/* otherwise sort by ip */
if (r == -2)
{
if (b == a)
r = 0;
else
r = (b > a) ? 1 : -1;
}
/*
printf("%d\n", r);
*/
return r;
}
/** acl_insert
* @param acl access list to modify
* @param ipstr ip/mask string
* @param nstr limit to place on class
* @returns -1 on error, 0 if new acl was added, 1 if existing acl was changed
*/
static int
acl_insert (acl_t * acl, char *ipstr, int count)
{
unsigned int ip, mask;
LIST **access_list = &acl->list;
access_t *acc;
LIST *list;
int r;
if (!is_address (ipstr, &ip, &mask))
return -1;
for (; *access_list; access_list = &(*access_list)->next)
{
acc = (*access_list)->data;
r = compare_ipmask (htonl (BSWAP32 (ip)), htonl (BSWAP32 (mask)),
htonl (BSWAP32 (acc->ip)),
htonl (BSWAP32 (acc->mask)));
if (r == 0)
{
acc->count = count;
return 1;
}
else if (r > 0)
break;
}
acc = CALLOC (1, sizeof (access_t));
if (!acc)
{
OUTOFMEMORY ("acl_insert");
return -1;
}
acc->ip = ip;
acc->mask = mask;
acc->count = count;
list = CALLOC (1, sizeof (LIST));
if (!list)
{
FREE (acc);
OUTOFMEMORY ("acl_insert");
return -1;
}
list->next = *access_list;
*access_list = list;
list->data = acc;
acl->oracle[ip & 0xff] |= (ip | ~mask);
return 0;
}
static int
generic_acl_load (acl_t * acl)
{
FILE *fp;
int ac;
char *av[2], *ptr, path[_POSIX_PATH_MAX];
int line = 0;
snprintf (path, sizeof (path), "%s/%s", Config_Dir, acl->name);
if (!(fp = fopen (path, "r")))
{
if (errno != ENOENT)
logerr ("generic_acl_load", path);
return -1;
}
while (fgets (Buf, sizeof (Buf) - 1, fp))
{
line++;
ptr = Buf;
while (ISSPACE (*ptr))
ptr++;
if (*ptr == '#' || *ptr == 0)
continue;
ac = split_line (av, FIELDS (av), Buf);
if (ac < 1)
continue;
if (acl_insert (acl, av[0], (ac > 1) ? atoi (av[1]) : 0) == -1)
{
log ("generic_acl_load:%s:%d:error parsing line:%s %s",
path, line, av[0], (ac > 1) ? av[1] : "");
}
}
fclose (fp);
return 0;
}
static acl_t *
get_acl (int tag)
{
if (tag == MSG_CLIENT_DLINE_ADD || tag == MSG_CLIENT_DLINE_DEL
|| tag == MSG_CLIENT_DLINE_LIST)
return &dlines;
if (tag == MSG_CLIENT_ELINE_ADD || tag == MSG_CLIENT_ELINE_DEL
|| tag == MSG_CLIENT_ELINE_LIST)
return &elines;
if (tag == MSG_CLIENT_ILINE_ADD || tag == MSG_CLIENT_ILINE_DEL
|| tag == MSG_CLIENT_ILINE_LIST)
return &ilines;
if (tag == MSG_CLIENT_CLASS_ADD || tag == MSG_CLIENT_CLASS_DEL
|| tag == MSG_CLIENT_CLASS_LIST)
return &limits;
return 0;
}
static void
generic_acl_init (acl_t * acl, const char *name, int flags)
{
memset (acl, 0, sizeof (acl));
acl->name = STRDUP (name);
acl->flags = flags;
}
void
acl_init (void)
{
generic_acl_init (&limits, "limit", F_SYNC);
generic_acl_load (&limits);
generic_acl_init (&ilines, "iline", 0);
generic_acl_load (&ilines);
generic_acl_init (&dlines, "dline", 0);
generic_acl_load (&dlines);
generic_acl_init (&elines, "eline", 0);
generic_acl_load (&elines);
}
/* ??? [:sender] <host>[/<mask>] [arg]
*/
HANDLER (generic_acl_add)
{
int ac;
char *av[2];
char *sender_name;
USER *sender;
acl_t *acl;
int count, modified = 0;
(void) len;
if (pop_user_server (con, tag, &pkt, &sender_name, &sender))
return;
if (sender && sender->level < LEVEL_MODERATOR)
return;
ac = split_line (av, FIELDS (av), pkt);
if (ac < 1)
return;
acl = get_acl (tag);
count = (ac > 1) ? atoi (av[1]) : 0;
modified = acl_insert (acl, av[0], count);
if (modified == -1)
{
if (ISUSER (con))
send_cmd (con, MSG_SERVER_NOSUCH, "unable to parse ip/mask");
return;
}
notify_mods (CHANGELOG_MODE, "%s%s %s %s on %s (%d)",
sender ? "" : "Server ",
sender_name,
modified ? "modified" : "added", acl->name, av[0], count);
if (acl->flags & F_SYNC)
pass_message_args (con, tag, ":%s %s %d", sender_name, av[0], count);
generic_acl_save (acl);
}
/* ??? [:sender] <host>[/<mask>] */
HANDLER (generic_acl_del)
{
char *host;
unsigned int ip, mask;
LIST **access_list;
LIST *tmp;
char *sender_name;
USER *sender;
access_t *acc;
acl_t *acl;
(void) len;
if (pop_user_server (con, tag, &pkt, &sender_name, &sender))
return;
if (sender && sender->level < LEVEL_MODERATOR)
return;
host = next_arg (&pkt);
if (!host)
return;
if (!is_address (host, &ip, &mask))
return;
acl = get_acl (tag);
access_list = &acl->list;
if (!check_oracle (acl, ip))
{
if (ISUSER (con))
send_cmd (con, MSG_SERVER_NOSUCH, "no matching ip addresses");
return;
}
for (; *access_list; access_list = &(*access_list)->next)
{
acc = (*access_list)->data;
if (mask == acc->mask && (ip & mask) == (acc->ip & acc->mask))
{
tmp = *access_list;
*access_list = (*access_list)->next;
FREE (tmp);
break;
}
}
/* rebuild the oracle */
memset (acl->oracle, 0, sizeof (int) * 256);
for (tmp = acl->list; tmp; tmp = tmp->next)
{
acc = tmp->data;
acl->oracle[acc->ip & 0xff] |= (acc->ip | ~acc->mask);
}
notify_mods (CHANGELOG_MODE, "%s%s removed %s on %s",
sender ? "" : "Server ", sender_name, acl->name, host);
if (acl->flags & F_SYNC)
pass_message_args (con, tag, ":%s %s", sender_name, host);
generic_acl_save (acl);
}
/* ??? */
HANDLER (generic_acl_list)
{
access_t *acc;
acl_t *acl;
char maskstr[sizeof ("/xxx.xxx.xxx.xxx")];
struct in_addr in;
LIST *list;
(void) len;
(void) pkt;
CHECK_USER_CLASS ("generic_acl_list");
if (con->user->level < LEVEL_MODERATOR)
return;
acl = get_acl (tag);
for (list = acl->list; list; list = list->next)
{
acc = list->data;
maskstr[0] = 0;
if (acc->mask != 0xffffffff)
{
in.s_addr = BSWAP32 (acc->mask);
snprintf (maskstr, sizeof (maskstr), "/%s", inet_ntoa (in));
}
in.s_addr = BSWAP32 (acc->ip);
send_cmd (con, tag, "%s%s %d", inet_ntoa (in), maskstr, acc->count);
}
send_cmd (con, tag, "");
}
static void
generic_acl_sync (CONNECTION * con, int tag, acl_t * acl)
{
LIST *list;
access_t *c;
struct in_addr in;
char maskstr[sizeof ("/xxx.xxx.xxx.xxx")];
ASSERT (validate_connection (con));
for (list = acl->list; list; list = list->next)
{
c = list->data;
maskstr[0] = 0;
if (c->mask != 0xffffffff)
{
in.s_addr = BSWAP32 (c->mask);
snprintf (maskstr, sizeof (maskstr), "/%s", inet_ntoa (in));
}
in.s_addr = BSWAP32 (c->ip);
send_cmd (con, tag, ":%s %s%s %d", Server_Name,
inet_ntoa (in), maskstr, c->count);
}
}
void
acl_sync (CONNECTION * con)
{
generic_acl_sync (con, MSG_CLIENT_CLASS_ADD, &limits);
}
static void
acl_free (acl_t * acl)
{
list_free (acl->list, free_pointer);
FREE (acl->name);
}
void
acl_destroy (void)
{
acl_free (&limits);
acl_free (&dlines);
acl_free (&ilines);
acl_free (&elines);
}